package com.wa.es.my;
import cn.hutool.core.lang.Console;
import com.wa.es.constant.ElasticQueryConstant;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.*;
import org.elasticsearch.index.search.MatchQuery;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.LinkedList;
import java.util.List;

public class QueryTest {

    static RestHighLevelClient highLevelClient;

    private String productIndex = "product_alias_test";

    private String wareIndex = "entity_partnum_test_v5";

    @Before
    public void getClient() throws Exception {
        // Low Level Client init
        highLevelClient = new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.1.105", 9200, "http")));
    }

    @After
    public void close() throws Exception {
        // Low Level Client init
        highLevelClient.close();
    }

    /**
     *  分词精确查询 一次匹配一个值
     */
    @Test
    public void termQuery(){
        //前面带1匹配不上
        QueryBuilder queryBuilder = QueryBuilders.termQuery("partNumber","GCM32EC71E226KE36W");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  分词精确查询 一次匹配多个值
     */
    @Test
    public void termsQuery(){
        QueryBuilder queryBuilder = QueryBuilders.termsQuery("partNumber","MCN12FD330J","06035A331FAT2A");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }




    /**
     *  是直接以一个短语的形式查询，而如果你在创建索引所使用的field的value中没有这么一个短语（顺序无差，且连接在一起），那么将查询不出任何结果。;
     *  要完全一直才能查询出来,MCN12FD330J能查询出来  MCN12FD330查询不出来
     */
    @Test
    public void matchPhraseQuery(){
        MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery(ElasticQueryConstant.CATEGORY_NAME_SEPARATE,"固定电感");
        matchPhraseQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_BUILD_IN_WHITESPACE);
        matchPhraseQueryBuilder.boost(1);
        matchPhraseQueryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(matchPhraseQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }


    /**
     *  matchAllQuery()方法用来匹配全部文档
     */
    @Test
    public void matchAllQuery(){
        QueryBuilder queryBuilder=QueryBuilders.matchAllQuery();
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  matchQuery("filedname","value")匹配单个字段，匹配字段名为filedname,值为value的文档
     *  在执行查询时，“小别克老”会被分词器分词，例如paoding解析成“小别/别克/老"
     *  and必须全部满足
     */
    @Test
    public void matchQueryAnd(){
        //前面带1也能匹配
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(ElasticQueryConstant.PARAM_DESC, "25V Y5V 0603");
        matchQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_PRODUCT_DESC);
        //则会自动为词组组成 match phrase查询。   ----测试修改为true结果一样
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        //----and不起作用
        matchQueryBuilder.minimumShouldMatch("100%");
        matchQueryBuilder.boost(1);
        matchQueryBuilder.operator(Operator.AND);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(matchQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  matchQuery("filedname","value")匹配单个字段，匹配字段名为filedname,值为value的文档
     *  在执行查询时，“小别克老”会被分词器分词，例如paoding解析成“小别/别克/老"
     *  or 搭配minimumShouldMatch使用
     */
    @Test
    public void matchQueryOr(){
        //前面带1也能匹配
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery(ElasticQueryConstant.PARAM_DESC, "25V Y5V 0603");
        matchQueryBuilder.analyzer(ElasticQueryConstant.ANALYSIS_PRODUCT_DESC);
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("90%");
        matchQueryBuilder.boost(1);
        matchQueryBuilder.operator(Operator.OR);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(matchQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    /**
     *  一个字段匹配多个值值,查询不出结果,待测试
     */
    @Test
    public void multiMatchQuery(){
        QueryBuilder queryBuilder = QueryBuilders.multiMatchQuery("partNumber",
                "MCN12FD330J");//搜索name中或interest中包含有music的文档（必须与music一致）
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }


    /**
     *  模糊查询，?匹配单个字符，*匹配多个字符
     */
    @Test
    public void wildcardQuery(){
        WildcardQueryBuilder queryBuilder = QueryBuilders.wildcardQuery("partNumber",
                "*MCN12FD330J*");//搜索名字中含有jack文档（name中只要包含jack即可）
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }


    /**
     *  匹配分词前缀 如果字段没分词，就匹配整个字段前缀
     */
    @Test
    public void prefixQuery(){
        QueryBuilder queryBuilder = QueryBuilders.prefixQuery("partNumber","MCN12FD");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(0)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(queryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }



    @Test
    public void boolQueryBuilder(){
        WildcardQueryBuilder queryBuilder1=QueryBuilders.wildcardQuery("partNumber","*GCM32EC71H106MA03*");//搜索名字中含有jack的文档
        WildcardQueryBuilder queryBuilder2=QueryBuilders.wildcardQuery("productId","*402881f96111adb7016111e1429c3*");//搜索interest中含有read的文档
        BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
        //name中必须含有jack,interest中必须含有read,相当于and
        boolQueryBuilder.must(queryBuilder1);
        boolQueryBuilder.should(queryBuilder2);
        boolQueryBuilder.minimumShouldMatch(1);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(1)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = getSearchHits(productIndex,searchSourceBuilder);
        Console.log(searchResponse);
    }

    @Test
    public void should(){
        BoolQueryBuilder boolQueryBuilder  = QueryBuilders.boolQuery();
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("paramDesc","0803");
        matchQueryBuilder.operator(Operator.OR);
        matchQueryBuilder.analyzer("ware_desc_analyzer");
        matchQueryBuilder.fuzzyTranspositions(true);
        matchQueryBuilder.lenient(false);
        matchQueryBuilder.zeroTermsQuery(MatchQuery.ZeroTermsQuery.NONE);
        matchQueryBuilder.autoGenerateSynonymsPhraseQuery(false);
        matchQueryBuilder.minimumShouldMatch("1");
        matchQueryBuilder.boost(5.0F);
        boolQueryBuilder.should(matchQueryBuilder);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.from(1)
                .size(10)
                .sort(SortBuilders.scoreSort().order(SortOrder.DESC))
                .query(boolQueryBuilder);
        SearchResponse searchResponse = getSearchHits("entity_partnum_test_v5",searchSourceBuilder);
        Console.log(searchResponse);
    }


    public static SearchResponse getSearchHits(String indexName, SearchSourceBuilder searchSourceBuilder){
        SearchRequest searchRequest = new SearchRequest(indexName);
        searchSourceBuilder.trackTotalHits(Boolean.TRUE);

        //https://www.elastic.co/guide/cn/elasticsearch/guide/current/relevance-intro.html#explain
        //输出 explain 结果代价是十分昂贵的，它只能用作调试工具 。千万不要用于生产环境。
        //builder.setExplain(true);
        Console.log("index name:{}",indexName);
        Console.log("执行查询语句:{}",searchSourceBuilder.toString());
        searchRequest.source(searchSourceBuilder);

        return getResponseData(searchRequest);
    }

    /**
     * 执行查询
     * @param searchRequest
     * @return
     */
    public static SearchResponse getResponseData(SearchRequest searchRequest) {
        SearchResponse searchResponse = null;
        try {
            long startTime = System.currentTimeMillis();
            searchResponse = highLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            long endTime = System.currentTimeMillis();
            Console.log("执行搜索查询时间:{}",(endTime-startTime));
        } catch (Exception e) {
            Console.log("执行查询异常{}",e);
            e.printStackTrace();
        }
        return searchResponse;
    }
}
